package com.android.equipment.utils

import android.annotation.SuppressLint
import android.media.MediaPlayer
import com.android.equipment.AlamViewModel
import com.android.equipment.R
import com.android.equipment.base.BaseApplication
import com.android.equipment.bean.RealDataBean
import com.android.equipment.dao.Data
import com.android.equipment.dao.DeviceModelAlamConfig
import com.android.equipment.dao.DeviceNode
import com.android.equipment.dao.HistoryAlam
import com.android.equipment.dao.RealTimeAlam
import com.android.equipment.dao.WorkDetail
import com.android.equipment.dao.operate.DaoConfigModel
import com.android.equipment.dao.operate.DaoData
import com.android.equipment.dao.operate.DaoDevice
import com.android.equipment.dao.operate.DaoDeviceModel
import com.android.equipment.dao.operate.DaoDeviceModelAlamConfig
import com.android.equipment.dao.operate.DaoDeviceNode
import com.android.equipment.dao.operate.DaoHistoryAlam
import com.android.equipment.dao.operate.DaoRealTimeAlam
import com.android.equipment.dao.operate.DaoWork
import com.android.equipment.dao.operate.DaoWorkDetail
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.SPUtils
import com.blankj.utilcode.util.StringUtils
import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.ToastUtils
import org.json.JSONObject
import java.math.RoundingMode
import java.text.SimpleDateFormat
import java.util.Date

object DataTreatUtil {
    private val deviceModelAlamList: MutableList<DeviceModelAlamConfig> by lazy {
        mutableListOf()
    }

    private val realDataList: MutableList<RealDataBean> by lazy {
        mutableListOf()
    }

    private val notifyRealDataUpdateMap: MutableMap<String, MutableList<RealDataBean>> by lazy {
        mutableMapOf()
    }
    val mPlayer: MediaPlayer by lazy {
        MediaPlayer.create(BaseApplication.getContext(), R.raw.alam)
    }

    val exigencyPlay:MediaPlayer by lazy {
        MediaPlayer.create(BaseApplication.getContext(), R.raw.alam2)
    }

    fun parse(data: String) {
        deviceModelAlamList.clear()
        val jsonObject = JSONObject()
        LogUtils.e("data====$data")
        data.apply {
            val indexOf = indexOf("=")
            val substring = substring(indexOf + 1, length)
            val list = substring.split(",")
            // TODO: 先进行校验和 查看这个数据对不对
            LogUtils.e("list====$list")
            if (!checkSum(list)){
                return
            }

            LogUtils.e("有用的数据来了")
            val dataArray = hexStringToBinaryArray(list[2])
            if (dataArray.isEmpty()){
                return
            }
            val devType = dataArray.copyOfRange(0, 6)
            val devSn = BinaryParseUtil.asciiToStr(devType)
            //根据当前的设备号 找到对应的设备  如果当前的设备在软件中 没有配置 就不去处理这条数据
            val device = DaoDevice.getInstance().getDeviceByDeviceCode(devSn)
            if (device == null){
                LogUtils.e("当前设备未配置")
                return
            }

            jsonObject.put("devSn", devSn)
            val dataType = dataArray.copyOfRange(6, dataArray.size)

            var i = 0
            val cmdType = dataType.copyOfRange(i, ++i)
            val cmd = BinaryParseUtil.getIntFromBytes(cmdType, 0)
            jsonObject.put("cmd", cmd)
            val lenType = dataType.copyOfRange(i, ++i)
            val len = BinaryParseUtil.getIntFromBytes(lenType, 0)
            val batType = dataType.copyOfRange(i, ++i)
            val bat = BinaryParseUtil.getIntFromBytes(batType, 0)
            jsonObject.put("bat", bat)
            val sbatType1 = dataType.copyOfRange(i, ++i)
            val sbatType2 = dataType.copyOfRange(i, ++i)
            val sbatType3 = dataType.copyOfRange(i, ++i)
            val sbatType4 = dataType.copyOfRange(i, ++i)
            val sbatType5 = dataType.copyOfRange(i, ++i)
            val sbat1 = BinaryParseUtil.getIntFromBytes(sbatType1, 0)
            val sbat2 = BinaryParseUtil.getIntFromBytes(sbatType2, 0)
            val sbat3 = BinaryParseUtil.getIntFromBytes(sbatType3, 0)
            val sbat4 = BinaryParseUtil.getIntFromBytes(sbatType4, 0)
            val sbat5 = BinaryParseUtil.getIntFromBytes(sbatType5, 0)
            jsonObject.put("sbat1", sbat1)
            jsonObject.put("sbat2", sbat2)
            jsonObject.put("sbat3", sbat3)
            jsonObject.put("sbat4", sbat4)
            jsonObject.put("sbat5", sbat5)

            val powType = dataType.copyOfRange(i, (i + 2).also { i = it })
            val powFromBytes = BinaryParseUtil.getIntFromBytes(powType, 0)
            val powArray = BinaryParseUtil.parseBinaryArray(powFromBytes, 16)
            val pow = powArray[0]
            jsonObject.put("pow", pow)
            val check = powArray[1]
            jsonObject.put("check", check)
            val alm1 = powArray[2]
            val alm2 = powArray[3]
            val alm3 = powArray[4]
            val alm4 = powArray[5]
            val walm1 = powArray[6]
            val walm2 = powArray[7]
            val walm3 = powArray[8]
            val walm4 = powArray[9]
            val walm5 = powArray[10]

            jsonObject.put("alm1", alm1)
            jsonObject.put("alm2", alm2)
            jsonObject.put("alm3", alm3)
            jsonObject.put("alm4", alm4)
            jsonObject.put("walm1", walm1)
            jsonObject.put("walm2", walm2)
            jsonObject.put("walm3", walm3)
            jsonObject.put("walm4", walm4)
            jsonObject.put("walm5", walm5)

            val deviceModelId = DaoDevice.getInstance().getDeviceModelId(device.deviceCode)
            val deviceModel = DaoDeviceModel.getInstance().getDeviceModel(deviceModelId)
            addAramInfo(deviceModel.modelCode, "alm1", alm1)
            addAramInfo(deviceModel.modelCode, "alm2", alm2)
            addAramInfo(deviceModel.modelCode, "alm3", alm3)
            addAramInfo(deviceModel.modelCode, "alm4", alm4)
            addAramInfo(deviceModel.modelCode, "walm1", walm1)
            addAramInfo(deviceModel.modelCode, "walm2", walm2)
            addAramInfo(deviceModel.modelCode, "walm3", walm3)
            addAramInfo(deviceModel.modelCode, "walm4", walm4)
            addAramInfo(deviceModel.modelCode, "walm5", walm5)
            addAramInfo(deviceModel.modelCode, "pow",pow)

            val gps = powArray[11]
            jsonObject.put("gps", gps)
            val lonType = dataType.copyOfRange(i, (i + 4).also { i = it })
            val lon = BinaryParseUtil.bytesToFloat(lonType, 0, 6).toDouble()
            val latType = dataType.copyOfRange(i, (i + 4).also { i = it })
            val lat = BinaryParseUtil.bytesToFloat(latType, 0, 6).toDouble()
            jsonObject.put("lon", lon)
            jsonObject.put("lat", lat)
//            if (gps == 1) {
//                //定位成功，需要更新设备的定位
//                updateDevicePoit(device.getId(), lon, lat)
//                super.addErrInfo(deviceModel.getModelCode(), "gps", gps, list, null)
//            }
            val unixTimeType = dataType.copyOfRange(i, (i + 4).also { i = it })
            val timestamp = BinaryParseUtil.bytesToUInt32(unixTimeType, 0)

            val unixTime = stampToDate(timestamp, "yyyy-MM-dd HH:mm:ss")
            jsonObject.put("unixTime", unixTime)

            val highType = dataType.copyOfRange(i, (i + 4).also { i = it })
            var high = BinaryParseUtil.bytesToFloat(highType, 0, 6).toDouble()
            var height = if (StringUtils.isEmpty(SPUtils.getInstance().getString("height"))) "0.00".toDouble() else SPUtils.getInstance().getString("height").toDouble()
            var startHeight = if (StringUtils.isEmpty(SPUtils.getInstance().getString("startHeight"))) "0.00".toDouble() else SPUtils.getInstance().getString("startHeight").toDouble()
            jsonObject.put("startHeight", startHeight)
            jsonObject.put("1152Height", height)
            jsonObject.put("realHeight", high)

//            high =  maxOf(high, 0.0)
//            height = maxOf(height, 0.0)
//            startHeight = maxOf(startHeight, 0.0)

            val resultHeight = high -(height - startHeight)
            //high -= height.toDouble()
            jsonObject.put("high", maxOf(resultHeight, 0.0))

            //将传递过来的数据存储到 数据库中
            val dataId = addProjectData(jsonObject.toString(), devSn)
            //处理数据  最终返回的是一个map   key == 作业id,设备号   value = 要展示的节点数据的对象值

            val map = handleRealTimeData(dataId)
            if (map.isNotEmpty()){
                //通知界面更新实时数据的值
                AlamViewModel.getInstance().realData.postValue(map)
            }

            if (deviceModelAlamList.isNotEmpty()) {  //如果根据当前的设备号 找到了对应的设备 并且已经把数据重新组装过了
                deviceModelAlamList.forEach {
                    //根据设备号和节点名称 查找设备节点表中 这个节点的数据
                    val deviceNode = DaoDeviceNode.getInstance().getDeviceNode(devSn, it.node)
                    //获取本地配置 查看当前的节点 在本地的报警值是多少
                    val modelAlamConfig = DaoDeviceModelAlamConfig.getInstance().getDeviceModelAlamConfig(it.device_model, it.node)
                    if (null == modelAlamConfig) { //当前的设备号 不在报警配置表中  证明软件没有这个设备  直接就跳出循环  不处理这条数据
                        return
                    } else {  //报警配置表有当前的设备号  需要处理
                        if (null == deviceNode) { //证明当前这个设备节点 在设备节点表中(上次报警数据) 不存在  证明当前这个设备号的节点数据 是第一次进来的
                            if (it.value == modelAlamConfig.node_value) {//当前的节点值 和 报警配置表中的节点值 进行比对  如果一致  证明这个节点是报警状态
                                addDeviceNode(it,devSn)//添加到设备节点表中  方便下次比对
                                val realTimeAlamIds = addRealTimeAlam(it,devSn,jsonObject)//添加到实时报警表中
                                addHistoryAlam(it,devSn,realTimeAlamIds)//添加到历史报警表中
                            } else {
                                //当前节点没报警 上次的报警数据中也没有
                                return@forEach
                            }
                        } else {
                            //证明在上次报警数据中存在

                            if (modelAlamConfig.node_value == it.value) { //证明当前节点报警了
                                if (it.value != deviceNode.node_value) { //跟上次的报警数据做比对 发现不一样  说明现在又重新报警了
                                    addDeviceNode(it,devSn) //因为恢复报警后就删除了 所以再次添加
                                    //入实时报警的表中
                                    val realTimeAlamIds = addRealTimeAlam(it,devSn,jsonObject)
                                    //入历史报警的表中
                                    addHistoryAlam(it,devSn, realTimeAlamIds)

                                } else { //跟上次的报警数据一致
                                    return@forEach
                                }

                            } else {  //证明当前的节点
                                if (it.value != deviceNode.node_value) { //跟上次的报警数据做比对 发现不一样  说明现在不报警了
                                    deviceNode.node_value = it.value
                                    DaoDeviceNode.getInstance()
                                        .deleteDeviceNode(deviceNode)  //删除设备节点表
                                    // 删除的是当前正在进行中的作业下的实时报警数据
                                    val realTimeAlamIds = deleteRealTimeAlam(it,devSn)

                                    // 变更历史报警 变跟当前进行中的作业下的历史报警  也就是刚才删除的实时数据相关的
                                    updateHistoryAlam(realTimeAlamIds)
                                } else { //跟上次的报警数据一致
                                    return@forEach
                                }

                            }
                        }
                    }

                }


            }
            //AlamViewModel.getInstance().alamData.value = jsonObject.toString()
        }

    }

    @SuppressLint("SimpleDateFormat")
    fun stampToDate(stamp: Long, format: String?): String {
        val sdf = SimpleDateFormat(format) //这个是你要转成后的时间的格式
        val date = Date(stamp * 1000)
        val sd: String = sdf.format(date)
        return sd
    }


    private fun addProjectData(jsonTxt: String, devSn: String): Long {
        val dataProject = Data()
        dataProject.txt_json = jsonTxt
        dataProject.create_time = TimeUtils.getNowMills()
        dataProject.device_code = devSn
        return DaoData.getInstance().addData(dataProject)
    }

    private fun addAramInfo(modelCode: String, node: String, value: Int) {
        val modelAlamConfig =
            DaoDeviceModelAlamConfig.getInstance().getDeviceModelAlamConfig(modelCode, node)
        modelAlamConfig.value = value
        modelAlamConfig.time = TimeUtils.getNowString()
        deviceModelAlamList.add(modelAlamConfig)
    }

    private fun addDeviceNode(item: DeviceModelAlamConfig,deviceCode: String) {
        val deviceNode = DeviceNode()
        deviceNode.node = item.node
        deviceNode.node_value = item.value
        deviceNode.deviceCode = deviceCode
        DaoDeviceNode.getInstance().addDeviceNode(deviceNode)
    }

    //当前的报警节点 可能在多个 进行中的 作业中出现 需要把对应的进行中的作业的该设备的节点 都添加到历史报警表中
    private fun addHistoryAlam(item: DeviceModelAlamConfig,deviceCode: String, realTimeAlamIds: MutableMap<Long, WorkDetail>) {
        realTimeAlamIds.forEach { (realTimeAlamId, detail) ->
            val historyAlam = HistoryAlam()
            historyAlam.device_code = deviceCode
            historyAlam.device_name = detail.deviceName
            historyAlam.work_id = detail.workId
            historyAlam.work_name = detail.workName
            historyAlam.people_name = detail.peopleName
            historyAlam.node = item.node
            historyAlam.node_des = item.node_des
            historyAlam.node_value = item.node_value
            historyAlam.alam_time = item.time
            historyAlam.real_time_alam_id = realTimeAlamId
            DaoHistoryAlam.getInstance().addHistoryAlam(historyAlam)
        }


    }


    private fun updateHistoryAlam(realTimeAlamIds: MutableMap<Long, Long>){
        realTimeAlamIds.forEach{ (realTimeAlamId, workId) ->
            DaoHistoryAlam.getInstance().updateHistoryAlamByRealTimeId(
                workId,
                realTimeAlamId,
                TimeUtils.getNowString()
            )
        }
    }

    //当前的节点的设备号 极有可能在多个正在 进行中 的作业中出现  需要把对应的进行中的作业的该设备的节点 都添加到实时报警表中
    private fun addRealTimeAlam(item: DeviceModelAlamConfig,deviceCode: String, jsonObject: JSONObject): MutableMap<Long, WorkDetail> {
        val realTimeAlamIds: MutableMap<Long, WorkDetail> = mutableMapOf()
        val progressWorkList = DaoWork.getInstance().getWorkByWorkStatus(1)
        progressWorkList.forEach {
            //根据作业ID 和 设备号 去查询 有几个作业 是有当前节点对应的设备的
            val progressWorkDetailList = DaoWorkDetail.getInstance()
                .getWorkDetailByWorkIdAndDeviceCode(it.id, deviceCode)
            progressWorkDetailList.forEach { detail ->  //一个作业中 最多存在一个该设备  所以progressWorkDetailList的数量正常是1
                val realTimeAlam = RealTimeAlam()
                realTimeAlam.workId = detail.workId
                realTimeAlam.node = item.node
                realTimeAlam.node_value = item.node_value
                realTimeAlam.alam_time = item.time
                realTimeAlam.node_des = item.node_des
                realTimeAlam.device_code = deviceCode
                val realTimeAlamId = DaoRealTimeAlam.getInstance().addRealTimeAlam(realTimeAlam)
                realTimeAlamIds[realTimeAlamId] = detail
                LogUtils.e("pow======================>${jsonObject.get("pow")}")
                if (jsonObject.get("alm1") == 1 && jsonObject.get("walm1") == 1){  //保险绳挂钩 和 围杆带挂钩 报警
                    exigencyPlay.start()
                    AlamViewModel.getInstance().errorAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                }else if (jsonObject.get("pow") == 0){  //关机
                    exigencyPlay.start()
                    AlamViewModel.getInstance().errorAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                }else if (jsonObject.get("walm1") == 1 && jsonObject.get("walm4") == 1){ //保险绳挂钩 和 围杆带挂钩 报警
                    exigencyPlay.start()
                    AlamViewModel.getInstance().errorAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                }else if (jsonObject.get("walm3") == 1){ //绳缓冲包
                    exigencyPlay.start()
                    AlamViewModel.getInstance().errorAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                } else {
                    mPlayer.start()
                    AlamViewModel.getInstance().realAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                }


            }
        }

        return realTimeAlamIds

    }

    // 删除也应该删除的是当前正在进行中的作业下的 根据设备号查到的 该节点的 实时报警数据
    private fun deleteRealTimeAlam(item: DeviceModelAlamConfig,deviceCode: String): MutableMap<Long, Long> {
        val realTimeAlamIds: MutableMap<Long, Long> = mutableMapOf()

        //查询出当前正在进行中的作业
        val progressWorkList = DaoWork.getInstance().getWorkByWorkStatus(1)
        progressWorkList.forEach {
            //查询出当前正在进行中的作业的详情
            val progressWorkDetailList = DaoWorkDetail.getInstance().getWorkDetailByWorkIdAndDeviceCode(it.id, deviceCode)
            // TODO: 此处有报错
            progressWorkDetailList.forEach { detail ->
                //删除前 先获取该实时报警的id  因为objectBOx 删除并不会返回该ID
                LogUtils.e("workId==${detail.workId}\ndeviceCode==$deviceCode\nnode==${item.node}")
                val realTimeAlamId = DaoRealTimeAlam.getInstance().getRealTimeAlamId(detail.workId, deviceCode, item.node)
                if (realTimeAlamId != null){
                    //删除当前设备节点的实时报警数据
                    DaoRealTimeAlam.getInstance().deleteRealTimeAlam(realTimeAlamId)
                    realTimeAlamIds[realTimeAlamId] = detail.workId
                    //通知刷新某个节点为恢复
                    AlamViewModel.getInstance().realAlamData.value = "${detail.workId},$deviceCode,${item.node}"
                }

            }
        }
        return realTimeAlamIds
    }

    //处理数据  查看当前的这个设备 是否在正在进行中的作业中  如果在的话 推送刷新页面
    private fun handleRealTimeData(id: Long): MutableMap<String, MutableList<RealDataBean>> {
        realDataList.clear()
        notifyRealDataUpdateMap.clear()

        val data = DaoData.getInstance().getDataById(id)
        val deviceCode = data.device_code
        val progressWorkList = DaoWork.getInstance().getWorkByWorkStatus(1)
        //根据设备号 获取到该设备的设备型号ID
        val deviceModelId = DaoDevice.getInstance().getDeviceModelId(deviceCode)

        val deviceModel = DaoDeviceModel.getInstance().getDeviceModel(deviceModelId)
        progressWorkList.forEach {
            realDataList.clear()
            //根据正在进行中的作业ID 和 设备号 查询出 正在作业中的该设备号的作业详情 有几条  一个作业中只有一个设备号  假设两个正在进行中的作业中都有这个设备号 也就是最多只有两条  感觉正常作业不会有这种操作出现,但是从软件的角度出发 是有可能的
            val progressWorkDetailList = DaoWorkDetail.getInstance().getWorkDetailByWorkIdAndDeviceCode(it.id, deviceCode)
            if (progressWorkDetailList.isNotEmpty()) { //代表此时有该设备的数据在正在进行中的作业中
                val jsonObject = JSONObject(data.txt_json)
                jsonObject.keys().forEach { key ->
                    //根据设备型号ID 和 node 查询出当前节点的配置
                    val configModel = DaoConfigModel.getInstance()
                        .getConfigModelByDeviceIdAndNode(deviceModelId, key)

                    //设备报警配置
                    val deviceModelAlamConfig = DaoDeviceModelAlamConfig.getInstance()
                        .getDeviceModelAlamConfig(deviceModel.modelCode, key)
                    if (configModel != null) { //如果配置表中存在该节点
                        if (configModel.is_show == 1) { //配置表中 设置的当前节点是显示状态
                            val realDataBean = RealDataBean()
                            realDataBean.node = key
                            realDataBean.node_des = configModel.node_des
                            realDataBean.node_unit = configModel.uni
                            if (configModel.is_bool == 1) {  //如果配置表中设置的is_bool是1  数据传过来的值是和报警配置表的值一致 显示文字的报警  否则显示正常
                                realDataBean.node_value = if (jsonObject.getLong(key) == deviceModelAlamConfig.node_value.toLong()) configModel.true_val else configModel.false_val
                            } else {
                                realDataBean.node_value = jsonObject.get(key).toString()
                            }
                            if (configModel.is_decimal > 0) {
                                realDataBean.node_value =
                                    jsonObject.get(key).toString().toBigDecimal()
                                        .setScale(configModel.is_decimal, RoundingMode.HALF_DOWN).toPlainString()
                            }

                            if (!StringUtils.isEmpty(configModel.relation_node)) {
                                val relationNodeValue =
                                    jsonObject.get(configModel.relation_node).toString()
                                val relationNodeConfig = DaoConfigModel.getInstance()
                                    .getConfigModelByDeviceIdAndNode(
                                        deviceModelId,
                                        configModel.relation_node
                                    )
                                realDataBean.relation_node = configModel.relation_node
                                realDataBean.relation_node_value = if (relationNodeValue == 255.toString()) "未开启" else relationNodeValue
                                realDataBean.relation_node_des = relationNodeConfig.node_des
                                realDataBean.relation_node_unit = if (relationNodeValue == 255.toString()) "" else relationNodeConfig.uni
                            }
                            realDataList.add(realDataBean)
                        }
                    }
                }
                val mapKey = "${it.id},$deviceCode"
                notifyRealDataUpdateMap[mapKey] = realDataList
            }
        }

        return notifyRealDataUpdateMap
    }





    private fun hexStringToBinaryArray(hexString: String): ByteArray {
        // 确保16进制字符串长度是偶数
        if ( hexString.length % 2 != 0){
            LogUtils.e("hexStringToBinaryArray hexString is not even length")
            return ByteArray(0)
        }

        // 将16进制字符串转换为字节数组
        val byteArray = ByteArray(hexString.length / 2)
        LogUtils.e("hexStringToBinaryArray hexString is $hexString, byteArray is ${GsonUtils.toJson(byteArray)}")
        for (i in byteArray.indices) {
            val index = i * 2
            byteArray[i] = ((hexString[index].digitToInt(16) shl 4) + hexString[index + 1].digitToInt(16)).toByte()
        }
        return byteArray
    }


    private fun checkSum(list: List<String>): Boolean{
        if (list.size != 6){
            return false
        }
        val serialNumber = list[0]
        val deviceCode = hexStringToDecimalSum(list[1])
        val data = hexStringToDecimalSum(list[2])
        val fieldStrength = list[3]
        val xinZaoBi = list[4]
        val checkSum =list[5].replace("\r","").replace("\n","")
        val sum = serialNumber.toInt()+ deviceCode + data +fieldStrength.toInt()+xinZaoBi.toInt()
        LogUtils.e("checkSum sum is $sum, checkSum is $checkSum")
        return  sum == checkSum.toInt()
    }


    fun hexStringToDecimalSum(hexString: String): Int {
        // 将16进制字符串转换为字节数组
        val byteArray = hexString.chunked(2)  // 每两位字符代表一个字节
            .map { it.toInt(16).toByte() } // 将16进制字符串转为字节

        // 计算字节数组每个元素的十进制值的和
        return byteArray.sumOf { it.toInt() and 0xFF } // 使用 & 0xFF 保证是无符号的十进制
    }
}